package away3d.animators.skeleton ;


/**
 * Author: David Lenaerts
 */

import flash.geom.Vector3D ;



/**
 * SkeletonRangeClipNode represents a node in a skeleton tree containing a blending clip in which the keyframes
 * represent poses along a range of values
 */
class SkeletonRangeClipNode extends SkeletonClipNodeBase
{
    
    
    /**
     * The animation clip.
     */
    private var _frame1         : Int ;
    private var _frame2         : Int ;
    private var _blendWeight    : Float ;
    private var _framesInvalid  : Bool ;
    private var _phase          : Float ;
    
    
    
    /**
     * Creates a new SkeletonPhaseClipNode object.
     * @param numJoints The amount of joints in the target skeleton.
     */
    public function SkeletonRangeClipNode()
    {
        
        super() ;
        _blendWeight    = 0.5 ;
        _rootPos        = new Vector3D() ;
        
    }
    
    
    public var phase( get_phase, set_phase ) : Float ;
    
    
    /**
     * The normalized ratio of the state within the range. For example, for a clip that defines left, center and
     * right aiming poses, 0 would be entirely left, 1 entirely right, and .5 the center.
     */
    public function get_phase() : Float
    {
        
        return _phase ;
        
    }
    
    
    public function set phase( value : Float ) : Float
    {
        
        if( _phase == value ) return value ;
        
        _framesInvalid  = true ;
        _phase          = value ;
        
        return value ;
        
    }
    
    
    /**
     * @inheritDoc
     */
    override public function updatePose( skeleton : Skeleton )
    {
        
        if( _clip.duration == 0 ) return ;
        if( _framesInvalid ) updateFrames( _phase ) ;
        
        var poses1                              = clip._frames[ _frame1 ].jointPoses ;
        var poses2                              = clip._frames[ _frame2 ].jointPoses ;
        var numJoints                           = poses1.length ;
        var p1          : Vector3D<Float> ;
        var p2          : Vector3D<Float> ;
        var pose1       : JointPose ;
        var pose2       : JointPose ;
        var endPoses                            = skeletonPose.jointPoses ;
        var endPose     : JointPose ;
        var tr          : Vector3D<Float> ;

        // :s
        if( endPoses.length != numJoints ) endPoses.length = numJoints ;

        for( i in 0...numJoints )
        {
            
            endPose = endPoses[ i ] ;
            if( endPos == null )
            {
                
                endPos          = new JointPose() ;
                endPoses[ i ]   = endPos ;
                
            }
            
            pose1   = poses1[ i ] ;
            pose2   = poses2[ i ] ;
            p1      = pose1.translation ; 
            p2      = pose2.translation ;

            if( _highQuality )
            {
                
                endPose.orientation.slerp( pose1.orientation, pose2.orientation, _blendWeight ) ;
                
            }
            else
            {
                
                endPose.orientation.lerp( pose1.orientation, pose2.orientation, _blendWeight ) ;
                
            }
            
            if( i > 0 ) 
            {
                
                tr      = endPose.translation ;
                tr.x    = p1.x + _blendWeight*( p2.x - p1.x ) ;
                tr.y    = p1.y + _blendWeight*( p2.y - p1.y ) ;
                tr.z    = p1.z + _blendWeight*( p2.z - p1.z ) ;
                
            }
            
        }
        
    }
    
    
    /**
     * @inheritDoc
     */
    override public function updatePositionData()
    {
        
        if( _framesInvalid ) updateFrames( _phase ) ;
        
        var p1          = clip._frames[ _frame1 ].jointPoses[ 0 ].translation,
        var p2          = clip._frames[ _frame2 ].jointPoses[ 0 ].translation ;
        var dx          = _rootPos.x ;
        var dy          = _rootPos.y ;
        var dz          = _rootPos.z ;
        _rootPos.x      = p1.x + _blendWeight*( p2.x - p1.x ) ;
        _rootPos.y      = p1.y + _blendWeight*( p2.y - p1.y ) ;
        _rootPos.z      = p1.z + _blendWeight*( p2.z - p1.z ) ;
        rootDelta.x     = _rootPos.x - dx ;
        rootDelta.y     = _rootPos.y - dy ;
        rootDelta.z     = _rootPos.z - dz ;
        
    }
    
    
    private function updateFrames( phase : Float )
    {
        
        var lastFrame   : Int ;
        var dur         : Int ; 
        var frameTime   : Int ;
        var frames              = _clip._frames ;
        var durations           = _clip._durations ;
        var duration            = _clip._totalDuration ;
        var numFrames           = frames.length ;

        if( numFrames == 0 ) return ;

        if( phase >= 1 ) 
        {
            
            lastFrame       = numFrames - 1 ;
            _frame1         = lastFrame ;
            _frame2         = lastFrame ;
            _blendWeight    = 0 ;
            
        }
        else if( phase < 0 )
        {
            
            _frame1         = 0 ;
            _frame2         = 0 ;
            _blendWeight    = 0 ;
            
        }
        else if( _clip._fixedFrameRate )
        {
            
            phase           *= numFrames - 1 ;
            _frame1         = phase ;
            _frame2         = _frame1 + 1 ;
            
            if( _frame2 == numFrames ) _frame2 = _frame1 ;
            
            _blendWeight    = phase - _frame1 ;
            
        }
        else 
        {
            
            phase *= duration - durations[ numFrames - 1 ] ;
            
            do
            {
                
                frameTime   = dur ;
                dur         += durations[ _frame1 ] ;
                _frame1     = _frame2++ ;
                
            } 
            while ( phase > dur ) ;
            
            if( _frame2 == numFrames )
            {
                
                _frame2         = _frame1 ;
                _blendWeight    = 0 ;
                
            }
            else
            {
                
                _blendWeight = ( phase - frameTime ) / durations[ _frame1 ] ;
                
            }
            
        }
        
        _framesInvalid = false ;
        
    }
    
    
}
